package fr.obsmip.sedoo.client.ui.misc;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.maps.client.MapOptions;
import com.google.gwt.maps.client.MapTypeId;
import com.google.gwt.maps.client.MapWidget;
import com.google.gwt.maps.client.base.LatLng;
import com.google.gwt.maps.client.base.LatLngBounds;
import com.google.gwt.maps.client.events.click.ClickMapEvent;
import com.google.gwt.maps.client.events.click.ClickMapHandler;
import com.google.gwt.maps.client.mvc.MVCObject;
import com.google.gwt.maps.client.overlays.InfoWindow;
import com.google.gwt.maps.client.overlays.InfoWindowOptions;
import com.google.gwt.maps.client.overlays.Marker;
import com.google.gwt.maps.client.overlays.MarkerOptions;
import com.google.gwt.maps.client.overlays.Rectangle;
import com.google.gwt.maps.client.overlays.RectangleOptions;
import com.google.gwt.uibinder.client.UiConstructor;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;

import fr.obsmip.sedoo.client.message.Message;
import fr.obsmip.sedoo.client.ui.GeoSummaryView.Presenter;
import fr.obsmip.sedoo.client.ui.map.MarkerDefinition;
import fr.obsmip.sedoo.shared.domain.AbstractGeoSummaryDTO;
import fr.obsmip.sedoo.shared.domain.DatasetGeoSummaryDTO;
import fr.obsmip.sedoo.shared.domain.ExperimentalSiteGeoSummaryDTO;
import fr.sedoo.commons.shared.domain.GeographicBoundingBoxDTO;

public class GeoSummaryMap extends DockLayoutPanel 
{
	private HashMap<String, ArrayList<Marker>> datasetLayers = new HashMap<String, ArrayList<Marker>>();
	private HashMap<String, ArrayList<MVCObject<?>>> experimentalSiteLayers = new HashMap<String, ArrayList<MVCObject<?>>>();
	private InfoWindow iw;
	boolean displayDatasets=false;
	boolean displayExperimentalSites=false;

	public GeoSummaryMap(String width, String height) {
		super(Unit.PCT);
		setWidth(width);
		setHeight(height);
		drawMap();
		forceLayout();
	}

	@UiConstructor
	public GeoSummaryMap() {
		this("100%", "100%");
	}

	private MapWidget mapWidget;
	private Presenter presenter;

	private void drawMap() {
		LatLng center = LatLng.newInstance(0,0);
		MapOptions opts = MapOptions.newInstance();
		opts.setZoom(2);
		opts.setCenter(center);
		opts.setMapTypeId(MapTypeId.HYBRID);
		mapWidget = new MapWidget(opts);

		add(mapWidget);
		mapWidget.triggerResize();
	}

	@Override
	public void onResize() {
		super.onResize();
		mapWidget.triggerResize();
	}

	public void addMarkersForObservatory(AbstractGeoSummaryDTO dto, String observatoryName, MarkerDefinition markerDefinition)
	{
		if (dto.getBox().validate().isEmpty()== false)
		{
			return;
		}
		String title = "";

		if (dto instanceof DatasetGeoSummaryDTO)
		{
			if (containsIgnoreCase(dto.getResourceTitle(), observatoryName) == false)
			{
				title = observatoryName+" - "+ Message.INSTANCE.dataSet() +" - "+ dto.getResourceTitle();
			}
			else
			{
				title = Message.INSTANCE.dataSet() +" - "+ dto.getResourceTitle();
			}

			MarkerOptions options = MarkerOptions.newInstance();
			LatLng center =  getCenter(dto.getBox());
			options.setPosition(center);
			options.setIcon(markerDefinition.getDataSetMarkerUrl());
			options.setTitle(title);
			final Marker marker = Marker.newInstance(options);
			displayMarker(marker, displayDatasets);
			ArrayList<Marker> aux = datasetLayers.get(observatoryName);
			if (aux == null)
			{
				aux = new ArrayList<Marker>();
				datasetLayers.put(observatoryName, aux);
			}
			aux.add(marker);
			marker.addClickHandler(new MarkerClickHandler(title, dto, marker));
		}
		if (dto instanceof ExperimentalSiteGeoSummaryDTO)
		{
			RectangleOptions rectangleOptions = RectangleOptions.newInstance();
			rectangleOptions.setFillColor(markerDefinition.getColor());
			rectangleOptions.setStrokeColor(markerDefinition.getColor());
			rectangleOptions.setStrokeWeight(1);
			if (containsIgnoreCase(dto.getResourceTitle(), observatoryName) == false)
			{
				title = observatoryName+" - "+ Message.INSTANCE.experimentalSite() +" - "+ dto.getResourceTitle();
			}
			else
			{
				title = Message.INSTANCE.experimentalSite() +" - "+ dto.getResourceTitle();
			}

			LatLng c1 = LatLng.newInstance(new Double(dto.getBox().getNorthBoundLatitude()), new Double(dto.getBox().getEastBoundLongitude()));
			LatLng c2 = LatLng.newInstance(new Double(dto.getBox().getSouthBoundLatitude()), new Double(dto.getBox().getWestBoundLongitude()));
			rectangleOptions.setBounds(LatLngBounds.newInstance(c2,c1));
			Rectangle rectangle = Rectangle.newInstance(rectangleOptions);
			displayMarker(rectangle, displayExperimentalSites);
			ArrayList<MVCObject<?>> aux = experimentalSiteLayers.get(observatoryName);
			if (aux == null)
			{
				aux = new ArrayList<MVCObject<?>>();
				experimentalSiteLayers.put(observatoryName, aux);
			}
			aux.add(rectangle);

			MarkerOptions options = MarkerOptions.newInstance();
			LatLng center =  getCenter(dto.getBox());
			options.setPosition(center);
			options.setIcon(markerDefinition.getExperimentalSiteMarkerUrl());
			options.setTitle(title);
			final Marker marker = Marker.newInstance(options);
			displayMarker(marker, displayExperimentalSites);
			aux.add(marker);
			marker.addClickHandler(new MarkerClickHandler(title, dto, marker));
		}

		mapWidget.triggerResize();
	}

	private LatLng getCenter(GeographicBoundingBoxDTO box) 
	{
		LatLng center;
		if (box.isPoint())
		{
			center = LatLng.newInstance(new Double(box.getNorthBoundLatitude()), new Double(box.getEastBoundLongitude()));
		}
		else
		{
			Double lat = new Double(box.getNorthBoundLatitude())+new Double(box.getSouthBoundLatitude());
			lat = lat /2;

			Double lon = new Double(box.getEastBoundLongitude())+new Double(box.getWestBoundLongitude());
			lon = lon /2;

			center = LatLng.newInstance(lat, lon);
		}
		return center;
	}

	private boolean containsIgnoreCase(String title, String observatoryName) 
	{
		return title.toLowerCase().contains(observatoryName.toLowerCase());
	}

	public void showLayer(String observatoryName)
	{
		ArrayList<Marker> datasetMarkers = datasetLayers.get(observatoryName);
		ArrayList<MVCObject<?>> siteMarkers = experimentalSiteLayers.get(observatoryName);

		ArrayList<MVCObject<?>> aux = new ArrayList<MVCObject<?>>();
		if ((datasetMarkers != null) && displayDatasets)
		{
			aux.addAll(datasetMarkers);
		}

		if ((siteMarkers != null) && displayExperimentalSites)
		{
			aux.addAll(siteMarkers);
		}
		Iterator<MVCObject<?>> iterator = aux.iterator();
		while (iterator.hasNext()) {
			displayMarker(iterator.next(), true);
		}
	}
	
	private void displayMarker(Object object, boolean value)
	{
		if (value)
		{
			if (object instanceof Marker)
			{
				((Marker) object).setMap(mapWidget);
			}

			if (object instanceof Rectangle)
			{
				((Rectangle) object).setMap(mapWidget);
			}
		}
		else
		{
			if (object instanceof Marker)
			{
				((Marker) object).setMap((MapWidget) null);
			}

			if (object instanceof Rectangle)
			{
				((Rectangle) object).setMap((MapWidget) null);
			}
		}
	}

	public void hideLayer(String observatoryName)
	{
		Iterator<MVCObject<?>> iterator = getObservatoryMarkers(observatoryName).iterator();
		while (iterator.hasNext()) {
			displayMarker(iterator.next(), false);
		}

	}

	private ArrayList<MVCObject<?>> getObservatoryMarkers(String observatoryName) {
		ArrayList<Marker> datasetMarkers = datasetLayers.get(observatoryName);
		ArrayList<MVCObject<?>> siteMarkers = experimentalSiteLayers.get(observatoryName);

		ArrayList<MVCObject<?>> aux = new ArrayList<MVCObject<?>>();
		if (datasetMarkers != null)
		{
			aux.addAll(datasetMarkers);
		}

		if (siteMarkers != null)
		{
			aux.addAll(siteMarkers);
		}
		return aux;
	}



	private class MarkerClickHandler implements ClickMapHandler
	{

		private String title;
		private AbstractGeoSummaryDTO dto;
		private MVCObject<?> marker;


		public MarkerClickHandler(String title, AbstractGeoSummaryDTO dto, MVCObject<?> marker) 
		{
			this.title = title;
			this.dto = dto;
			this.marker = marker;

		}

		@Override
		public void onEvent(ClickMapEvent event) {

			if (marker == null || event.getMouseEvent() == null) {
				return;
			}

			HTML header = new HTML("<b>"+title+"</b>");

			VerticalPanel vp = new VerticalPanel();
			vp.setSpacing(5);
			vp.add(header);
			vp.add(new Label(dto.getResourceAbstract()));


			HorizontalPanel detailPanel = new HorizontalPanel();

			Label details = new Label(Message.INSTANCE.geoSummaryViewSeeDetails());
			details.addStyleName("rightPadding5px");
			detailPanel.add(details);

			if (dto instanceof DatasetGeoSummaryDTO)
			{
				Anchor datasetLink = new Anchor(Message.INSTANCE.dataSet());
				datasetLink.addStyleName("rightPadding5px");
				datasetLink.addClickHandler(new ClickHandler() {

					@Override
					public void onClick(ClickEvent event) {
						presenter.displayDataset(dto.getUuid());
					}
				});
				detailPanel.add(datasetLink);
			}
			Anchor experimentalSiteLink = new Anchor(Message.INSTANCE.experimentalSite());
			experimentalSiteLink.addStyleName("rightPadding5px");
			Anchor observatoryLink = new Anchor(Message.INSTANCE.observatory());
			if (dto instanceof DatasetGeoSummaryDTO)
			{
				experimentalSiteLink.addClickHandler(new ClickHandler() {

					@Override
					public void onClick(ClickEvent event) {
						presenter.displayExperimentalSiteFromDataset(dto.getUuid());
					}
				});
				
				observatoryLink.addClickHandler(new ClickHandler() {

					@Override
					public void onClick(ClickEvent event) {
						presenter.displayObservatoryFromDataset(dto.getUuid());
					}
				});
			}
			else if  (dto instanceof ExperimentalSiteGeoSummaryDTO)
			{
				experimentalSiteLink.addClickHandler(new ClickHandler() {

					@Override
					public void onClick(ClickEvent event) {
						presenter.displayExperimentalSite(dto.getUuid());
					}
				});
				
				observatoryLink.addClickHandler(new ClickHandler() {

					@Override
					public void onClick(ClickEvent event) {
						presenter.displayObservatoryFromExperimentalSite(dto.getUuid());
					}
				});
			}

		

			



			detailPanel.add(experimentalSiteLink);
			detailPanel.add(observatoryLink);
			vp.add(detailPanel);

			InfoWindowOptions options = InfoWindowOptions.newInstance();
			options.setContent(vp);

			if (iw != null)
			{
				iw.close();
			}

			iw = InfoWindow.newInstance(options);
			iw.open(mapWidget, marker);
		}

	}

	public void setPresenter(Presenter presenter) {
		this.presenter = presenter;
	}

	public void ensureVisible() {
		mapWidget.triggerResize();
	}

	public void displayExperimentalSites(Boolean value) {
		displayExperimentalSites = value;
		Iterator<String> observatoryIterator = experimentalSiteLayers.keySet().iterator();
		while (observatoryIterator.hasNext()) {
			String observatoryName = observatoryIterator.next();
			Iterator<MVCObject<?>> iterator = experimentalSiteLayers.get(observatoryName).iterator();
			while (iterator.hasNext()) 
			{
				displayMarker(iterator.next(), value);
			}
		}
	}

	public void displayDataSets(Boolean value) {
		displayDatasets = value;
		Iterator<String> observatoryIterator = datasetLayers.keySet().iterator();
		while (observatoryIterator.hasNext()) {
			String observatoryName = observatoryIterator.next();
			Iterator<Marker> iterator = datasetLayers.get(observatoryName).iterator();
			while (iterator.hasNext()) 
			{
				displayMarker(iterator.next(), value);
			}
		}
		
	}

}
